
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
//using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.Data;
using System.Drawing;

namespace WiccanRede
{
   // [ComVisible(true)]
    public struct SceneObject : ISceneObject
    {
        string name;
        string modelName;
        string modelShaderName;

        float x;
        float y;
        float z;

        float sx;
        float sy;
        float sz;

        float rx;
        float ry;
        float rz;

        ISceneObject next;

        public SceneObject(string name, string modelName, string modelShaderName)
        {
            this.name = name;
            this.modelName = modelName;
            this.modelShaderName = modelShaderName;
            this.next = null;

            this.x = 0;
            this.y = 0;
            this.z = 0;

            this.sx = 0;
            this.sy = 0;
            this.sz = 0;

            this.rx = 0;
            this.ry = 0;
            this.rz = 0;
        }

        public void SetPosition(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public void SetScale(float sx, float sy, float sz)
        {
            this.sx = sx;
            this.sy = sy;
            this.sz = sz;
        }

        public void SetRotation(float rx, float ry, float rz)
        {
            this.rx = rx;
            this.ry = ry;
            this.rz = rz;
        }

        public float GetRotationX()
        {
            return this.rx;
        }

        public float GetRotationY()
        {
            return this.ry;
        }

        public float GetRotationZ()
        {
            return this.rz;
        }

        public float GetScaleX()
        {
            return this.sx;
        }

        public float GetScaleY()
        {
            return this.sy;
        }

        public float GetScaleZ()
        {
            return this.sz;
        }

        public float GetX()
        {
            return this.x;
        }

        public float GetY()
        {
            return this.y;
        }

        public float GetZ()
        {
            return this.z;
        }

        public string GetName()
        {
            return this.name;
        }

        public string GetModelName()
        {
            return this.modelName;
        }

        public string GetModelShaderName()
        {
            return this.modelShaderName;
        }

        public void SetName(String name)
        {
            this.name = name;
        }

        public void SetModelName(String modelName)
        {
            this.modelName = modelName;
        }

        public void SetModelShaderName(String shaderName)
        {
            this.modelShaderName = shaderName;
        }

        public void SetNextItem(SceneObject next)
        {
            this.next = next;
        }

        public ISceneObject GetNextObject()
        {
            return next;
        }
    }

    //[ComVisible(true)]
    public struct SceneShader : ISceneShader
    {
        string shaderName;
        string filename;

        public SceneShader(String shaderName, String filename)
        {
            this.shaderName = shaderName;
            this.filename = filename;
        }

        public string GetShaderName()
        {
            return shaderName;
        }

        public void SetShaderName(String name)
        {
            this.shaderName = name;
        }

        public string GetShaderFilename()
        {
            return filename;
        }

        public void SetShaderFilename(String filename)
        {
            this.filename = filename;
        }
    }

    //[ComVisible(true)]
    public class SceneBuilder : ISceneBuilder
    {
        const float PIDivide180 = (float)(Math.PI / 180.0);

        List<ISceneObject> storedObjects;
        List<SceneShader> storedShaders;
        int currentModel = 0;
        int currentShader = 0;

        string mapPath = String.Empty;
        string map_color0 = String.Empty;
        string map_normal = String.Empty;
        float mapWidth = 1000;
        float mapHeight = 1000;

        public SceneBuilder()
        {
            storedObjects = new List<ISceneObject>();
            storedShaders = new List<SceneShader>();
        }

        public void SaveScene(string path)
        {
            XmlDocument xml = new XmlDocument();

            xml.CreateXmlDeclaration("1.0", null, null);

            XmlNode scene = xml.CreateNode(XmlNodeType.Element, "Scene", String.Empty);

            SaveShaders(xml);
            SaveObjects(xml);

            xml.AppendChild(scene);

            xml.Save(path);
        }

        public void LoadScene(string path)
        {

            XmlDocument xml = new XmlDocument();
            XmlTextReader xtr = null;
            try
            {
                xtr = new XmlTextReader(Path.GetFullPath(path));
                xml.Load(xtr);
            }
            catch (Exception ex)
            {
                //MessageBox.Show(String.Format("Scene Load: {0}", ex.Message));
            }
            finally
            {
                if (xtr != null)
                    xtr.Close();
            }

            XmlAttributeCollection sceneAttributes = xml.GetElementsByTagName("Scene")[0].Attributes;


            XmlNode mapNode = xml.GetElementsByTagName("Map")[0];
            XmlAttributeCollection mapAttributes = mapNode.Attributes;

            string resourcePath = sceneAttributes["resourcePath"].Value;
            string mapDirectory = mapAttributes["mapDirectory"].Value;
            string mapFile = mapAttributes["file"].Value;

            mapDirectory = String.Format("{0}{1}/", resourcePath, mapDirectory);

            this.mapPath = String.Format("{0}{1}", mapDirectory, mapFile);

            foreach (XmlNode n in mapNode.ChildNodes)
            {
                switch (n.Attributes["name"].Value)
                {
                    case "color0_texture":
                        this.map_color0 = String.Format("{0}{1}", mapDirectory, ProcessType<String>(n));
                        break;

                    case "normal_texture":
                        this.map_normal = String.Format("{0}{1}", mapDirectory, ProcessType<String>(n));
                        break;

                    case "width":
                        this.mapWidth = ProcessType<float>(n);
                        break;

                    case "height":
                        this.mapHeight = ProcessType<float>(n);
                        break;
                }

            }


            LoadShaders(xml);
            LoadObjects(xml);

           
        }

        public String GetMapTexturePath()
        {
            return this.map_color0;
        }

        public String GetMapNormalTexturePath()
        {
            return this.map_normal;
        }

        public float[,] LoadMap()
        {
            if (String.IsNullOrEmpty(this.mapPath) && File.Exists(this.mapPath))
                return null;

            Bitmap mapBitmap = (Bitmap)Image.FromFile(this.mapPath);
            Color[,] map = WiccanRede.Utils.BitmapOperations.BitmapToColorArray(mapBitmap);

            float[,] heightMap = new float[map.GetLength(0), map.GetLength(1)];

            int xMax = heightMap.GetLength(0);
            int yMax = heightMap.GetLength(1);
            
            for (int i = 0; i < xMax; i++)
            {
                for (int j = 0; j < yMax; j++)
                {
                    heightMap[i, j] = map[i, j].B;
                }
            }

            return heightMap;
        }

        private void SaveShaders(XmlDocument xml)
        {
            XmlNode shaders = xml.CreateNode(XmlNodeType.Element, "Shaders", String.Empty);

            foreach (var sceneShader in storedShaders)
            {
                XmlNode shader = xml.CreateNode(XmlNodeType.Element, "Shader", String.Empty);

                XmlAttribute name = xml.CreateAttribute("name");
                XmlAttribute file = xml.CreateAttribute("file");

                name.Value = sceneShader.GetShaderName();
                file.Value = sceneShader.GetShaderFilename();

                shader.Attributes.Append(name);
                shader.Attributes.Append(file);
                shaders.AppendChild(shader);
            }

            xml.GetElementsByTagName("Scene")[0].AppendChild(shaders);
        }

        private void SaveObjects(XmlDocument xml)
        {
            XmlNode objects = xml.CreateNode(XmlNodeType.Element, "Objects", String.Empty);

            XmlAttribute defaultShader = xml.CreateAttribute("defaultShader");
            defaultShader.Value = "default";
            objects.AppendChild(defaultShader);

            foreach (var sceneObject in storedObjects)
            {
                SaveObject(sceneObject, objects, xml);

                ISceneObject currentObject = sceneObject.GetNextObject();

                while (currentObject != null)
                {
                    SaveObject(currentObject, objects, xml);

                    currentObject = currentObject.GetNextObject();
                }
            }

            xml.GetElementsByTagName("Scene")[0].AppendChild(objects);
        }

        private void SaveObject(ISceneObject sceneObject, XmlNode parent, XmlDocument xml)
        {
            XmlNode obj = xml.CreateNode(XmlNodeType.Element, "Object", String.Empty);
            XmlAttribute name = xml.CreateAttribute("name");
            XmlAttribute model = xml.CreateAttribute("model");
            XmlAttribute shader = xml.CreateAttribute("shader");

            name.Value = sceneObject.GetName();
            model.Value = sceneObject.GetModelName();
            shader.Value = sceneObject.GetModelShaderName();

            obj.Attributes.Append(name);
            obj.Attributes.Append(model);

            if (!String.IsNullOrEmpty(shader.Value))
                obj.Attributes.Append(shader);


            XmlNode position = CreateVector3("position", sceneObject.GetX(), sceneObject.GetY(), sceneObject.GetZ(), xml);
            XmlNode scale = CreateVector3("scale", sceneObject.GetScaleX(), sceneObject.GetScaleY(), sceneObject.GetScaleZ(), xml);
            XmlNode rotation = CreateVector3("rotation", sceneObject.GetRotationX(), sceneObject.GetRotationY(), sceneObject.GetRotationZ(), xml);

            obj.AppendChild(position);
            obj.AppendChild(scale);
            obj.AppendChild(rotation);

            parent.AppendChild(obj);
        }

        private void LoadShaders(XmlDocument xml)
        {
            string resourcePath = xml.GetElementsByTagName("Scene")[0].Attributes["resourcePath"].Value;
            XmlNode shaders = xml.GetElementsByTagName("Shaders")[0];

            string shaderDirectory = shaders.Attributes["shaderDirectory"].Value;

            resourcePath = String.Format("{0}{1}/", resourcePath, shaderDirectory);

            foreach (XmlNode shader in shaders.ChildNodes)
            {
                string name = shader.Attributes["name"].Value;
                string file = shader.Attributes["file"].Value;

                file = Path.GetFullPath(String.Format("{0}{1}", resourcePath, file));

                SceneShader sceneShader = new SceneShader(name, file);
                storedShaders.Add(sceneShader);
            }
        }

        private void LoadObjects(XmlDocument xml)
        {
            XmlNode objects = xml.GetElementsByTagName("Objects")[0];

            String defaultShader = objects.Attributes["defaultShader"].Value;

            foreach (XmlNode obj in objects.ChildNodes)
            {
                string name = obj.Attributes["name"].Value;
                string modelName = obj.Attributes["model"].Value;
                string shader = (obj.Attributes["shader"] != null) ? obj.Attributes["shader"].Value : String.Empty;

                if (String.IsNullOrEmpty(shader))
                    shader = defaultShader;

                SceneObject storedObject = new SceneObject(name, modelName, shader);

                foreach (XmlNode unRequiredProperties in obj.ChildNodes)
                {
                    storedObject = ProcessObject(unRequiredProperties, storedObject);
                }

                foreach (SceneObject sobj in storedObjects)
                {
                    if (sobj.GetModelName() == modelName)
                    {
                        SceneObject temp = sobj;
                        while (sobj.GetNextObject() != null) temp = (SceneObject)sobj.GetNextObject();

                        temp.SetNextItem(storedObject);

                    }
                }

                storedObjects.Add(storedObject);
            }
        }

        private T ProcessType<T>(XmlNode node)
        {
            switch (node.Name)
            {
                case "Float":
                    float x;
                    Single.TryParse(node.Attributes["value"].Value, out x);
                    return (T)(Object)x;

                case "String":
                    return (T)(Object)node.Attributes["value"].Value;
            }

            return default(T);
        }

        private SceneObject ProcessObject(XmlNode node, SceneObject obj)
        {
            float x = 0;
            float y = 0;
            float z = 0;

            switch (node.Name)
            {
                case "Vector3":
                    #region Vector3 parsing
                    String value = node.Attributes["value"].Value;

                    List<float> floatValue = new List<float>();
                    int index = value.IndexOf(";");
                    while (true)
                    {
                        floatValue.Add(Convert.ToSingle(value.Substring(0, index)));
                        value = value.Remove(0, index + 1);
                        index = value.IndexOf(";");

                        if (index == -1)
                        {
                            floatValue.Add(Convert.ToSingle(value));
                            break;
                        }
                    }

                    x = floatValue[0];
                    y = floatValue[1];
                    z = floatValue[2];

                    #endregion
                        break;
            }

            switch (node.Attributes["name"].Value)
            {
                case "position":
                    obj.SetPosition(x, y, z);
                    break;

                case "scale":
                    obj.SetScale(x, y, z);
                    break;

                case "rotation":
                    x *= PIDivide180;
                    y *= PIDivide180;
                    z *= PIDivide180;
                    obj.SetRotation(x, y, z);
                    break;
            }

            return obj;
        }

        private XmlNode CreateVector3(String name, float x, float y, float z, XmlDocument xml)
        {
            XmlNode vector3 = xml.CreateNode(XmlNodeType.Element, "Vector3", String.Empty);
            XmlAttribute nameAttribute = xml.CreateAttribute("name");
            XmlAttribute valueAttribute = xml.CreateAttribute("value");

            nameAttribute.Value = name;
            valueAttribute.Value = String.Format("{0};{1};{2}", x, y, z);

            vector3.Attributes.Append(nameAttribute);
            vector3.Attributes.Append(valueAttribute);

            return vector3;
        }

        public int GetObjectCount()
        {
            return storedObjects.Count;
        }

        public ISceneObject GetCurrentObject()
        {
            return storedObjects[currentModel];
        }

        public bool NextObjectRow()
        {
            if (this.currentModel < this.storedObjects.Count)
            {
                this.currentModel++;
                return true;
            }

            return false;
        }

        public void SetNextObjectRow(ISceneObject sceneObject)
        {
            this.storedObjects.Add(sceneObject);
            this.currentModel++;
        }

        public void ResetObjectRow()
        {
            this.currentModel = 0;
        }

        public void ClearObjects()
        {
            storedObjects.Clear();
        }


        public int GetShadersCount()
        {
            return storedShaders.Count;
        }

        public ISceneShader GetCurrentShader()
        {
            return storedShaders[currentShader];
        }

        public bool NextShader()
        {
            if (this.currentShader < this.storedShaders.Count)
            {
                this.currentShader++;
                return true;
            }

            return false;
        }

        public void SetNextShader(SceneShader sceneShader)
        {
            this.storedShaders.Add(sceneShader);
            this.currentShader++;
        }

        public void ResetShaderPosition()
        {
            this.currentShader = 0;
        }

        public void ClearShaders()
        {
            storedShaders.Clear();
        }
    }
}
